//
// enum.cs: Enum handling.
//
// Author: Miguel de Icaza (miguel@gnu.org)
//         Ravi Pratap     (ravi@ximian.com)
//         Marek Safar     (marek.safar@seznam.cz)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
//
// Copyright 2001 Ximian, Inc (http://www.ximian.com)
// Copyright 2003-2003 Novell, Inc (http://www.novell.com)
// Copyright 2011 Xamarin Inc
//

using System;

using MetaType = System.Type;
using System.Reflection;

namespace Teal.Compiler {

	
	/// <summary>
	///   Enumeration container
	/// </summary>
	public partial class EnumDefinition : TypeDefinition
	{
		//
		// Implicit enum member initializer, used when no constant value is provided
		//
		sealed class ImplicitInitializer : Expression
		{
			readonly FieldDefinition prev;
            readonly FieldDefinition current;

            public ImplicitInitializer(FieldDefinition current, FieldDefinition prev)
			{
				this.current = current;
				this.prev = prev;
			}

			public override bool ContainsEmitWithAwait ()
			{
				return false;
			}

			public override Expression CreateExpressionTree (ResolveContext ec)
			{
				throw new NotSupportedException ("Missing Resolve call");
			}

			protected override Expression DoResolve (ResolveContext rc)
			{
				// We are the first member
				if (prev == null) {
					return NewExpression.Constantify (current.containerType.reference, Location);
				}

				var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant;
				try {
					return c.Increment ();
				} catch (OverflowException) {
					Compiler.Report.Error (543, current.startLocation2,
						"The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'",
						current.GetSignatureForError (), ((EnumDefinition) current.containerType).UnderlyingType.GetSignatureForError ());

					return NewExpression.Constantify (current.containerType.reference, current.startLocation2);
				}
			}

			public override void Emit (EmitContext ec)
			{
				throw new NotSupportedException ("Missing Resolve call");
			}
		}

		#region Properties

		public TypeReference UnderlyingType {
			get {
				return ((EnumReference) reference).UnderlyingType;
			}
		}

		#endregion

	    public static void Error_1008 (Location2 loc, Report Report)
		{
			Report.Error (1008, loc,
				"Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
		}

		protected override bool DoDefineMembers ()
		{
			for (int i = 0; i < Members.Count; ++i) {
				var em = (FieldDefinition)Members[i];
				if (em.Initializer == null) {
					em.Initializer = new ImplicitInitializer (em, i == 0 ? null :  (FieldDefinition)  Members[i - 1]);
				}

				em.Define ();
			}

			return true;
		}

		public override bool IsUnmanagedType ()
		{
			return true;
		}


        public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
            throw new NotImplementedException();
        }
    }

 public	class EnumReference : TypeReference
	{
		TypeReference underlying;

		public EnumReference (TypeReference declaringType, ITypeDefinition definition, TypeReference underlyingType, MetaType info, Modifiers modifiers)
			: base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.final)
		{
			this.underlying = underlyingType;
		}

		public TypeReference UnderlyingType {
			get {
				return underlying;
			}
			set {
				if (underlying != null)
					throw new InternalErrorException ("UnderlyingType reset");

				underlying = value;
			}
		}

		public static TypeReference GetUnderlyingType (TypeReference t)
		{
			return ((EnumReference) t.GetDefinition ()).UnderlyingType;
		}

		public static bool IsValidUnderlyingType (TypeReference type)
		{
			switch (type.BuiltinType) {
			case BuiltinTypeSpec.Type.Int:
			case BuiltinTypeSpec.Type.UInt:
			case BuiltinTypeSpec.Type.Long:
			case BuiltinTypeSpec.Type.Byte:
			case BuiltinTypeSpec.Type.SByte:
			case BuiltinTypeSpec.Type.Short:
			case BuiltinTypeSpec.Type.UShort:
			case BuiltinTypeSpec.Type.ULong:
				return true;
			}

			return false;
		}
	}
}
